package providers

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"xagent/internal/streaming"
)

// BailianProvider implements LLMProvider using Bailian's API
type BailianProvider struct {
	config     *ProviderConfig
	httpClient *http.Client
}

// NewBailianProvider creates a new Bailian provider
func NewBailianProvider(config *ProviderConfig) (*BailianProvider, error) {
	if config.APIKey == "" {
		return nil, fmt.Errorf("Bailian API key is required")
	}

	return &BailianProvider{
		config:     config,
		httpClient: &http.Client{},
	}, nil
}

// Complete implements LLMProvider
func (p *BailianProvider) Complete(ctx context.Context, prompt string, options *CompletionOptions) (string, error) {
	return p.Chat(ctx, []Message{{Role: "user", Content: prompt}}, &ChatOptions{
		Model:       options.Model,
		MaxTokens:   options.MaxTokens,
		Temperature: options.Temperature,
		TopP:        options.TopP,
		Stop:        options.Stop,
	})
}

// Chat implements LLMProvider
func (p *BailianProvider) Chat(ctx context.Context, messages []Message, options *ChatOptions) (string, error) {
	// TODO: Implement Bailian chat API
	return "", fmt.Errorf("not implemented")
}

// StreamChat implements LLMProvider
func (p *BailianProvider) StreamChat(ctx context.Context, messages []Message, options *ChatOptions) (<-chan streaming.StreamEvent, error) {
	ch := make(chan streaming.StreamEvent)

	go func() {
		defer close(ch)

		handler := &streaming.DefaultStreamHandler{}
		var result string
		handler.OnEventFunc = func(event *streaming.StreamEvent) error {
			result += event.Content
			ch <- *event
			return nil
		}

		if err := p.StreamChatWithHandler(ctx, messages, options, handler); err != nil {
			ch <- streaming.StreamEvent{
				Type:  "error",
				Error: err,
			}
		}
	}()

	return ch, nil
}

// StreamChatWithHandler implements LLMProvider
func (p *BailianProvider) StreamChatWithHandler(ctx context.Context, messages []Message, options *ChatOptions, handler streaming.StreamHandler) error {
	// TODO: Implement Bailian streaming chat API
	return fmt.Errorf("not implemented")
}

// GetConfig returns the provider configuration
func (p *BailianProvider) GetConfig() *ProviderConfig {
	return p.config
}

// Embed implements LLMProvider
func (p *BailianProvider) Embed(ctx context.Context, text string) ([]float32, error) {
	reqBody := map[string]interface{}{
		"model": "bailian-embedding",
		"input": text,
	}

	resp, err := p.makeRequest(ctx, "/embeddings", reqBody)
	if err != nil {
		return nil, err
	}

	return resp.Data[0].Embedding, nil
}

type bailianResponse struct {
	Choices []struct {
		Message struct {
			Content string `json:"content"`
		} `json:"message"`
	} `json:"choices"`
	Data []struct {
		Embedding []float32 `json:"embedding"`
	} `json:"data"`
}

func (p *BailianProvider) makeRequest(ctx context.Context, endpoint string, body map[string]interface{}) (*bailianResponse, error) {
	jsonBody, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %v", err)
	}

	req, err := http.NewRequestWithContext(
		ctx,
		"POST",
		p.config.BaseURL+endpoint,
		strings.NewReader(string(jsonBody)),
	)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %v", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+p.config.APIKey)

	resp, err := p.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to make request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(body))
	}

	var result bailianResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %v", err)
	}

	return &result, nil
}

// GetName implements LLMProvider
func (p *BailianProvider) GetName() string {
	return "bailian"
}
